package com.beiji.lib.pen.ui.view

import android.content.Context
import android.content.res.Resources
import android.graphics.*
import android.util.Log
import android.view.MotionEvent
import android.view.View
import com.beiji.lib.pen.PenLog
import com.beiji.lib.pen.R
import com.beiji.lib.pen.cache.PenDotCacheHelper
import com.beiji.lib.pen.model.DotUnit
import com.beiji.lib.pen.model.PenStroke
import com.beiji.lib.pen.ui.renderer.*
import com.beiji.lib.pen.utils.LruCacheUtils
import kotlinx.coroutines.CommonPool
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import java.lang.ref.SoftReference
import java.text.DecimalFormat


/**
 * 笔迹View 分页
 * Created by X on 2018/6/14.
 */
class StrokePageView(context: Context) : View(context), OnGestureListener {

    companion object {
        // 最小缩放倍数
        private const val minScale: Float = 1f

        // 最大缩放倍数
        private const val maxScale: Float = 2f

        private const val TAG = "StrokePageView"

    }

    enum class EdgeState {
        EDGE_NONE,
        EDGE_LEFT,
        EDGE_RIGHT,
        EDGE_BOTH
    }

    var pageIndex = 0
    var pageId = 0
    var bookId = 0

    private val textPaint = Paint()
    private val bgPaint = Paint()

    private lateinit var parentView: StrokeView
    private var mWidth: Int = 0
    private var mHeight: Int = 0

    private var maxForce = 600

    var strokeBitmap: Bitmap? = null
    var bgBitmap: Bitmap? = null
    var bgWrite: Bitmap? = null
    private val bitmapMatrix = Matrix()
    private var bitmapCanvas: Canvas = Canvas()
    private val eraserRenderer = EraserRenderer()

    //    private val renderer = TQLRenderer()
//    private val renderer = LineRenderer()
    private val renderer = PathRenderer()
    private val markRenderer = MarkPathRenderer()
    private var scaleDragDetector: ScaleDragDetector = ScaleDragDetector(context, this)
    private var mScrollEdge = EdgeState.EDGE_NONE

    var markBitmap: Bitmap? = null
    private var markCanvas: Canvas = Canvas()
    private var markDrawJob: Job? = null
    private lateinit var lruCacheUtils: LruCacheUtils

    constructor(context: Context, width: Int, height: Int, parentView: StrokeView) : this(context) {
        this.mWidth = width
        this.mHeight = height
        this.parentView = parentView
        eraserRenderer.baseStrokeWidth = parentView.baseStrokeWidth
        lruCacheUtils = LruCacheUtils.getInstance()
    }

    init {
        bgPaint.apply {
            style = Paint.Style.STROKE
            alpha = 70 //70%透明度
        }
        textPaint.apply {
            isAntiAlias = true
            color = Color.parseColor("#C8CED4")
            strokeWidth = 10F
            textSize = 10 * context.resources.displayMetrics.density
        }
//        maxForce = PenManager.instance.getConnectedPen()?.forceMax ?: 0
    }

    fun recycleBitmap() {
        if (bgWrite != null) {
            bgWrite?.recycle()
            bgWrite = null
            Log.i(TAG, "recycleBitmap: ")
        }

        if (strokeBitmap != null) {
            strokeBitmap?.recycle()
            strokeBitmap = null
        }
        if (bgBitmap != null) {
            bgBitmap?.recycle()
            bgBitmap = null
        }
    }

    private fun getPaint(): Paint {
        return parentView.paint
    }

    private fun getMarkPaint(): Paint {
        return parentView.paintMark
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        PenLog.e("onLayout:$top,$bottom")
        mWidth = right - left
        mHeight = bottom - top
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.save()
        canvas.drawFilter = PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
        drawBgWrite(canvas)
        // drawBackground(canvas)
        drawPageNum(canvas)
        drawStroke(canvas)
        drawMarkPreview(canvas)
        //renderer.draws(canvas, getPaint())
        canvas.restore()
    }

    private fun drawPageNum(canvas: Canvas) {
//        if (parentView.pageMap.size() == 1) {
//            return
//        }
        if (bookId == 100) {
            textPaint.strokeWidth = 10f
            textPaint.textSize = 15 * context.resources.displayMetrics.density
            textPaint.color = Color.GRAY
            val paddingBottom = 50 * context.resources.displayMetrics.density
            val pageIndexString = DecimalFormat("00").format(pageId + 1)
            if ((pageId + 1) % 2 == 0) {
                canvas.drawText(pageIndexString, mWidth * 0.08f, mHeight * 0.96f, textPaint)
            } else {
                canvas.drawText(pageIndexString, mWidth * 0.92f, mHeight * 0.96f, textPaint)
            }
            //  canvas.drawText(pageIndexString, (mWidth / 2).toFloat(), mHeight.toFloat() - paddingBottom, textPaint)
            val dp_1 = 1 * context.resources.displayMetrics.density
            textPaint.strokeWidth = dp_1
            canvas.drawLine(0f, mHeight.toFloat() - dp_1, mWidth.toFloat(), mHeight.toFloat() - dp_1, textPaint)
        } else {
            textPaint.strokeWidth = 10f
            textPaint.textSize = 10 * context.resources.displayMetrics.density
            val paddingBottom = 21 * context.resources.displayMetrics.density
            textPaint.color = Color.parseColor("#C8CED4")
            var pageIndexString: String
            if (bookId == 1) {
                pageIndexString = DecimalFormat("00").format(pageId)
            } else {
                pageIndexString = DecimalFormat("00").format(pageId + 1)
            }
            canvas.drawText(pageIndexString, mWidth.toFloat() - 60, mHeight.toFloat() - paddingBottom, textPaint)
            val dp_1 = 1 * context.resources.displayMetrics.density
            textPaint.strokeWidth = dp_1
            canvas.drawLine(0f, mHeight.toFloat() - dp_1, mWidth.toFloat(), mHeight.toFloat() - dp_1, textPaint)
        }

    }

    private fun drawBackground(canvas: Canvas) {
        bgBitmap?.let {
            if (!it.isRecycled) {
                canvas.drawBitmap(it, 0f, 0f, bgPaint)
            }
        }
    }


    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        Log.i(TAG, "onSizeChanged: bookId=$bookId//pageId=$pageId")
    }

    //适配BookID 背景
    private fun drawBgWrite(canvas: Canvas) {
        if (parentView.isDrawDotBg) {
            if (mWidth == 0 || mHeight == 0) {
                return
            }
            if (bgWrite == null) {
                Log.i(TAG, "drawBgWrite: bgWrite == null")
                var bg_drawable = R.drawable.note_edit_bg
                if (bookId == 3) {
                    bg_drawable = R.drawable.note_edit_bg_a6_line
                } else if (bookId == 4) {
                    bg_drawable = R.drawable.note_edit_bg_a5_line
                } else if (bookId == 101) {
                    bg_drawable = R.drawable.note_edit_bg_a5_101
                } else if (bookId == 30) {
                    bg_drawable = R.drawable.note_edit_bg_a5_30
                } else if (bookId == 100 ) {
                    bg_drawable = R.drawable.techpage
                }

                if (!lruCacheUtils.isExistKey(bookId)) {
                    Log.i(TAG, "cacheBitmap == null: ")
                    val sr = SoftReference<Bitmap>(decodeBitmapResource(resources, bg_drawable))
                    var bmp = sr.get()
                    lruCacheUtils.addBitmapToMemoryCache(bookId, bmp)
                    bgWrite = Bitmap.createScaledBitmap(bmp!!, mWidth, mHeight, true)
                } else {
                    Log.i(TAG, "cacheBitmap != null: ")
                    val srf = SoftReference<Bitmap>(lruCacheUtils.getBitmapFromMemCache(bookId))
                    bgWrite = Bitmap.createScaledBitmap(srf.get()!!, mWidth, mHeight, true)
                }
//                //val sr = SoftReference<Bitmap>(BitmapFactory.decodeResource(this.context.resources, bg_drawable).copy(Bitmap.Config.ARGB_4444, true))
//                val sr = SoftReference<Bitmap>(decodeBitmapResource(resources,bg_drawable))
//                var bmp = sr.get()
//                bgWrite = Bitmap.createScaledBitmap(bmp, mWidth, mHeight, true)
            }
//            if (cacheBitmap == null) {
//                Log.i(TAG, "cacheBitmap == null")
//                var bg_drawable = R.drawable.note_edit_bg
//                if (bookId == 3) {
//                    bg_drawable = R.drawable.note_edit_bg_a6_line
//                } else if (bookId == 4) {
//                    bg_drawable = R.drawable.note_edit_bg_a5_line
//                } else if (bookId == 100) {
//                    bg_drawable = R.drawable.note_edit_bg_a5_100
//                }
//                val sr = SoftReference<Bitmap>(BitmapFactory.decodeResource(this.context.resources, bg_drawable).copy(Bitmap.Config.ARGB_4444, true))
//                var bmp = sr.get()
//                bgWrite = Bitmap.createScaledBitmap(bmp, mWidth, mHeight, true)
//                lruCacheUtils.addBitmapToMemoryCache(bookId, bgWrite)
//            } else {
//                Log.i(TAG, "cacheBitmap != null")
//                bgWrite = cacheBitmap
//            }
//            if (bgWrite == null) {
//                Log.i(TAG, "drawBgWrite: bgWrite == null")
//                var bg_drawable = R.drawable.note_edit_bg
//                if (bookId == 3) {
//                    bg_drawable = R.drawable.note_edit_bg_a6_line
//                } else if (bookId == 4) {
//                    bg_drawable = R.drawable.note_edit_bg_a5_line
//                } else if (bookId == 100) {
//                    bg_drawable = R.drawable.note_edit_bg_a5_100
//                }
//                val cacheBitmap = lruCacheUtils.getBitmapFromMemCache(bookId)
//                if (cacheBitmap == null) {
//                    Log.i(TAG, "cacheBitmap == null: ")
//                    val sr = SoftReference<Bitmap>(decodeBitmapResource(resources, bg_drawable))
//                    var bmp = sr.get()
//                    lruCacheUtils.addBitmapToMemoryCache(bookId, bmp)
//                    bgWrite = Bitmap.createScaledBitmap(bmp, mWidth, mHeight, true)
//                } else {
//                    Log.i(TAG, "cacheBitmap != null: ")
//                    bgWrite = Bitmap.createScaledBitmap(cacheBitmap, mWidth, mHeight, true)
//                }
////                //val sr = SoftReference<Bitmap>(BitmapFactory.decodeResource(this.context.resources, bg_drawable).copy(Bitmap.Config.ARGB_4444, true))
////                val sr = SoftReference<Bitmap>(decodeBitmapResource(resources,bg_drawable))
////                var bmp = sr.get()
////                bgWrite = Bitmap.createScaledBitmap(bmp, mWidth, mHeight, true)
//            }
            bgWrite?.let {
                if (!it.isRecycled) {
                    Log.i(TAG, "drawBgWrite: bgWrite != null")
                    canvas.drawBitmap(it, bitmapMatrix, null)
                }
            }
        }
    }

    fun decodeBitmapResource(resources: Resources, resId: Int): Bitmap? {
        val ips = resources.openRawResource(resId)
        val opts = BitmapFactory.Options()
        opts.inPurgeable = false
        opts.inInputShareable = true
        opts.inPreferredConfig = Bitmap.Config.ARGB_4444
        return BitmapFactory.decodeStream(ips, null, opts)
    }


    private fun drawMarkPreview(canvas: Canvas) {
        if (mWidth == 0 || mHeight == 0) {
            return
        }
        if (markBitmap == null) {
            markBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_4444)
        }
        markBitmap?.let {
            if (!it.isRecycled) {
                canvas.drawBitmap(it, bitmapMatrix, null)
            }
        }
    }

    fun drawDot(dot: DotUnit, hasBg: Boolean = false) {
        initCanvas()
        if (dot.actionType == PenStroke.ACTION_ERASER) {
            eraserRenderer.draw(bitmapCanvas, dot, null)
            postInvalidate()
        } else if (dot.actionType == PenStroke.ACTION_STROKE) {
            renderer.draw(bitmapCanvas, dot, getPaint())
            postInvalidate()
        } else if (dot.actionType == PenStroke.ACTION_MARK) {
//            markDrawJob?.cancel(null)
//            //马克笔因为绘制逻辑的关系，必须保证invalidate和draw在同一个线程，不然会出现马克笔闪烁的情况
//            markDrawJob = launch(UI) {
//                PenLog.e("mark", "draw mark")
//                markRenderer.draw(bitmapCanvas, dot, getMarkPaint(), markBitmap!!, markCanvas)
//                if (markDrawJob != null && !markDrawJob!!.isActive) {
//                    PenLog.e("mark", "cancelled.")
//                    return@launch
//                }
//                PenLog.e("mark", "invalidate.")
//                invalidate()
//            }

            markRenderer.draw(bitmapCanvas, dot, getMarkPaint(), markBitmap!!, markCanvas)
            postInvalidate()
        }
    }

    private fun doDrawStroke(stroke: PenStroke) {
        if (stroke.action == PenStroke.ACTION_ERASER) {
            eraserRenderer.draw(bitmapCanvas, stroke, parentView.dotTransformMatrix, null)
        } else if (stroke.action == PenStroke.ACTION_STROKE) {
            renderer.draw(bitmapCanvas, stroke, parentView.dotTransformMatrix, getPaint())
        } else if (stroke.action == PenStroke.ACTION_MARK) {

            markRenderer.draw(bitmapCanvas, stroke, parentView.dotTransformMatrix, getMarkPaint())
        }
    }

    private fun initCanvas() {
        if (strokeBitmap == null) {
//            mBufferQueue.offer(dot)
            strokeBitmap = loadCacheBitmap()
        }
        bitmapCanvas.setBitmap(strokeBitmap)
        if (markBitmap == null) {
            markBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_4444)
        }
        markCanvas.setBitmap(markBitmap)
    }

    private fun loadCacheBitmap(): Bitmap? {
        //TODO(在页面还没加载好的时候，要把点的数据缓存下来)
        if (mWidth == 0 || mHeight == 0) {
            return null
        }
        return Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_4444)
    }

//    private fun createBitmapIfNotExists(): Bitmap {
//        return Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_4444)
//    }
//

    fun clearStroke() {
        strokeBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_4444)
        renderer.reset()
        markRenderer.reset()
        postInvalidate()
    }

    fun reset() {
        renderer.reset()
        markRenderer.reset(bitmapCanvas, markBitmap!!, markCanvas)
    }

    fun drawStroke(stroke: PenStroke) {
        initCanvas()
        doDrawStroke(stroke)
        postInvalidate()
    }

//    fun restorePenStroke(strokeList: MutableList<PenStroke>) = runBlocking{
//        initCanvas()
//        val jobs = arrayListOf<Job>()
//        for (i in 0 until strokeList.size) {
//            jobs += async(CommonPool) {
//                val penStroke = strokeList[i]
//                val lineSegments = penStroke.convertToPenStroke()
//                lineSegments.forEach {
//                    it.scale(mWidth, mHeight, PaperSize.B5)
//                    bitmapCanvas.drawLine(it.x1.toFloat(), it.y1.toFloat(),
//                            it.x2.toFloat(), it.y2.toFloat(), getPaint())
//                }
//                if (i == strokeList.size - 1) {
//                    runBlocking(UI) {
//                        postInvalidate()
//                    }
//                }
//            }
//        }
//        jobs.forEach { it.join() }
//    }

    fun restorePenStroke(strokeList: MutableList<PenStroke>?) {
        if (strokeList == null || strokeList.isEmpty()) {
            return
        }
        initCanvas()
        for (i in 0 until strokeList.size) {
            launch(CommonPool) {
                //                doDrawStroke(strokeList[i])

                //TODO 通过TQLRenderer多协程同时绘制会出现无法解释的问题
                val stroke = strokeList[i]
                if (stroke.action == PenStroke.ACTION_ERASER) {
                    eraserRenderer.draw(bitmapCanvas, stroke, parentView.dotTransformMatrix, null)
                } else {
                    var paint: Paint = getPaint()
                    if (stroke.action == PenStroke.ACTION_MARK) {
                        paint = getMarkPaint()
                    }
                    //多线程同时绘制，不能共用一个TQLRenderer,否则数据会串
                    TQLRenderer().draw(bitmapCanvas, stroke, parentView.dotTransformMatrix, paint)
                }

//                val penStroke = strokeList[i]
//                val lineSegments = penStroke.convertToPenStroke()
//                lineSegments.forEach {
//                    it.scale(mWidth, mHeight, PaperSize.B5)
//                    bitmapCanvas.drawLine(it.x1.toFloat(), it.y1.toFloat(),
//                            it.x2.toFloat(), it.y2.toFloat(), getPaint())
//                }
                if (i == strokeList.size - 1) {
                    postInvalidate()
                }
            }
        }
    }

    private fun exportBitmap(withBg: Boolean): Bitmap? {
        if (!withBg) {
            return strokeBitmap
        }
        val bitmap =bgBitmap?.copy(Bitmap.Config.ARGB_4444, true)
        val canvas = Canvas(bitmap!!)
        canvas.drawBitmap(strokeBitmap!!, 0f, 0f, getPaint())
        return bitmap
    }

    fun exportBitmap(): Bitmap? {
        return exportBitmap(false)
    }

    private fun drawStroke(canvas: Canvas) {
        strokeBitmap?.let { bitmap ->
            if (!bitmap.isRecycled) {
                canvas.drawBitmap(bitmap, bitmapMatrix, getPaint())
            }
            eraserRenderer.eraserIndicator.let {
                if (it.isVisible) {
                    canvas.drawCircle(it.centerX, it.centerY, it.radius, it.eraserIndicatorPaint)
                }
            }
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (event.pointerCount > 1 || getCurScaleFactor() != 1f || parentView.getEditMode() == StrokeView.EditMode.ERASER) {
            parent.requestDisallowInterceptTouchEvent(true)
        }

        val action = event.action and MotionEvent.ACTION_MASK
        //在后续的事件到来之前，并不清楚是单指擦除还是双指拖动或者缩放，所以要把down事件一起传给scaleDragDetector和Eraser
        if (action == MotionEvent.ACTION_DOWN) {
            scaleDragDetector.onTouchEvent(event)

            initCanvas()
            if (eraserRenderer.pageId <= 0) {
                eraserRenderer.pageId = pageId
                eraserRenderer.bookId = bookId
            }
            PenDotCacheHelper.acttype = PenStroke.ACTION_ERASER
            eraserRenderer.setEraserRadius(parentView.getEraserRadius())
            if (parentView.getEditMode() == StrokeView.EditMode.ERASER) {
                eraserRenderer.onTouchEvent(bitmapCanvas, bitmapMatrix, parentView.dotTransformMatrix, event)
            }
            //隐藏橡皮擦
            eraserRenderer.eraserIndicator.isVisible = parentView.getEditMode() == StrokeView.EditMode.ERASER
            invalidate()
        } else {
            //之前的拖动、缩放状态
            val wasDragOrScale = scaleDragDetector.isScaling || scaleDragDetector.isDragging
            //非橡皮擦模式 或者 已经在拖动、缩放状态  或者  多指触摸  这三种情况下 Touch事件都交由scaleDragDetector处理
            //isDragOrScale || event.pointerCount > 1 缺一不可，因为会有一些临界情况，比如，多指操作但是isDragOrScale状态未变，又或者isDragOrScale为true，但是一个手指已经抬起
            if (parentView.getEditMode() != StrokeView.EditMode.ERASER || wasDragOrScale || event.pointerCount > 1) {
                scaleDragDetector.onTouchEvent(event)
            }
            //当前的拖动、缩放状态
            val isDragOrScale = scaleDragDetector.isScaling || scaleDragDetector.isDragging
            if (parentView.getEditMode() == StrokeView.EditMode.ERASER && !isDragOrScale) {
                eraserRenderer.onTouchEvent(bitmapCanvas, bitmapMatrix, parentView.dotTransformMatrix, event)
                invalidate()
            }

        }
        return true
    }


    override fun onDrag(dx: Float, dy: Float) {
        onDragAction(dx, dy)
    }

    override fun onFling(startX: Float, startY: Float, velocityX: Float, velocityY: Float) {}

    override fun onScale(scaleFactor: Float, focusX: Float, focusY: Float) {
        onScaleAction(scaleFactor, focusX, focusY)
    }

    private fun getCurScaleFactor(): Float {
        val values = FloatArray(9)
        bitmapMatrix.getValues(values)
        return values[Matrix.MSCALE_X]
    }

    // 缩放
    private fun onScaleAction(scaleFactor: Float, focusX: Float, focusY: Float): Boolean {
        PenLog.e("scaleFactor:$scaleFactor")
        val curScaleFactor = getCurScaleFactor()
        if (curScaleFactor < maxScale && scaleFactor > 1.0f || curScaleFactor > minScale && scaleFactor < 1.0f) {
            var resultScaleFactor = scaleFactor
            if (curScaleFactor * scaleFactor < minScale) {
                resultScaleFactor = minScale / curScaleFactor
            }
            if (curScaleFactor * scaleFactor > maxScale) {
                resultScaleFactor = maxScale / curScaleFactor
            }
            // 以手指所在地方进行缩放
            bitmapMatrix.postScale(resultScaleFactor, resultScaleFactor,
                    focusX, focusY)
            checkMatrixBounds("Scale", bitmapMatrix)
            invalidate()

        }

        return false
    }

    // 拖动
    private fun onDragAction(dx: Float, dy: Float): Boolean {
        bitmapMatrix.postTranslate(dx, dy)
        checkMatrixBounds("drag", bitmapMatrix)
        invalidate()
        return false
    }

    private fun checkMatrixBounds(source: String, matrix: Matrix): Boolean {
        PenLog.d("source:$source")
        val rect = getDisplayRect(matrix) ?: return false
        PenLog.e("rect:" + rect.toString())

        val height = rect.height()
        val width = rect.width()
        var deltaX = 0f
        var deltaY = 0f

        val viewHeight = mHeight
//        if (height <= viewHeight) {
//
//        } else
        if (rect.top > 0) {
            deltaY = -rect.top
        } else if (rect.bottom < viewHeight) {
            deltaY = viewHeight - rect.bottom
        }

        val viewWidth = mWidth
        if (width <= viewWidth) {
            deltaX = -rect.left
            mScrollEdge = EdgeState.EDGE_BOTH
        } else if (rect.left > 0) {
            mScrollEdge = EdgeState.EDGE_LEFT
            deltaX = -rect.left
        } else if (rect.right < viewWidth) {
            deltaX = viewWidth - rect.right
            mScrollEdge = EdgeState.EDGE_RIGHT
        } else {
            mScrollEdge = EdgeState.EDGE_NONE
        }

        matrix.postTranslate(deltaX, deltaY)
        PenLog.i("deltaX:$deltaX , deltaY:$deltaY")
        val rectAfter = getDisplayRect(matrix) ?: return false
        PenLog.i("rectAfter:" + rectAfter.toString())
        return true
    }

    private fun getDisplayRect(matrix: Matrix): RectF? {
        val mDisplayRect = RectF()
        mDisplayRect.set(0f, 0f, mWidth.toFloat(), mHeight.toFloat())
        matrix.mapRect(mDisplayRect)
        return mDisplayRect
    }

}